package tau.adx.common.bids.generators;

import java.util.Collection;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.Map;
import java.util.Random;

import lombok.Data;
import tau.adx.common.AdxConstants;
import tau.adx.common.adn.AdNetwork;
import tau.adx.common.adn.generators.SimpleAdNetworkGenerator;
import tau.adx.common.ads.generators.SimpleAdGenerator;
import tau.adx.common.bids.AdBidInfo;
import tau.adx.common.bids.BidInfo;
import tau.adx.common.bids.BidTarget;
import tau.adx.common.bids.bundles.AdxBidBundle;
import tau.adx.common.generators.GenericGenerator;
import tau.adx.common.publishers.AdxPublisher;
import tau.adx.common.users.AdxUser;

/**
 * A naive implementation of the {@link GenericGenerator} interface. Randomizes
 * each characteristic of the {@link AdxBidBundle}.
 * 
 * @author greenwald
 * 
 */
@Data
public class SimpleBidBundleGenerator implements GenericGenerator<AdxBidBundle> {

	/**
	 * Indicates whether minor attributes, such as {@link AdxUser}'s pContinue,
	 * should be set to default value.
	 */
	private boolean ignoreMinorAttributes;
	/**
	 * {@link Collection} of {@link AdxPublisher publishers} to generate an
	 * {@link AdxBidBundle} for.
	 */
	private final Collection<AdxPublisher> publishers;

	/**
	 * An instance of the {@link Random} class.
	 */
	private Random random;

	/**
	 * {@link Collection} of {@link AdxUser users} to generate an
	 * {@link AdxBidBundle} for.
	 */
	private final Collection<AdxUser> users;

	/**
	 * @param users
	 *            {@link Collection} of {@link AdxUser users} to generate an
	 *            {@link AdxBidBundle} for.
	 * @param publishers
	 *            {@link Collection} of {@link AdxPublisher publishers} to
	 *            generate an {@link AdxBidBundle} for.
	 * @param ignoreMinorAttributes
	 *            Indicates whether minor attributes, such as {@link AdxUser}'s
	 *            pContinue, should be set to default value.
	 */
	public SimpleBidBundleGenerator(Collection<AdxUser> users,
			Collection<AdxPublisher> publishers, boolean ignoreMinorAttributes) {
		super();
		this.users = users;
		this.publishers = publishers;
		this.ignoreMinorAttributes = ignoreMinorAttributes;
		random = new Random();
	}

	/**
	 * @see tau.adx.common.generators.GenericGenerator#generate(int)
	 */
	@Override
	public Collection<AdxBidBundle> generate(int amount) {
		Collection<AdxBidBundle> bundles = new LinkedList<>();
		for (int i = 0; i < amount; i++) {
			bundles.add(getRandomAdxBidBundle(users, publishers));
		}
		return bundles;
	}

	/**
	 * @param users
	 *            {@link Collection} of {@link AdxUser users} to generate an
	 *            {@link AdxBidBundle} for.
	 * @param publishers
	 *            {@link Collection} of {@link AdxPublisher publishers} to
	 *            generate an {@link AdxBidBundle} for.
	 * @return A {@link Random} {@link AdxBidBundle}.
	 */
	private AdxBidBundle getRandomAdxBidBundle(Collection<AdxUser> users,
			Collection<AdxPublisher> publishers) {
		AdNetwork adNetwork = SimpleAdNetworkGenerator.getRandomAdNetwork();
		Map<BidTarget, BidInfo> bundles = randomBundles(users, publishers,
				adNetwork);
		AdxBidBundle adxBidBundle = new AdxBidBundle(bundles, adNetwork);
		return adxBidBundle;
	}

	/**
	 * @param users
	 *            {@link Collection} of {@link AdxUser users} to generate an
	 *            {@link AdxBidBundle} for.
	 * @param publishers
	 *            {@link Collection} of {@link AdxPublisher publishers} to
	 *            generate an {@link AdxBidBundle} for.
	 * @param adNetwork
	 *            {@link AdNetwork} bidding for the bundle.
	 * @return A {@link Map} between {@link BidTarget} and {@link BidInfo}.
	 */
	private Map<BidTarget, BidInfo> randomBundles(Collection<AdxUser> users,
			Collection<AdxPublisher> publishers, AdNetwork adNetwork) {
		Map<BidTarget, BidInfo> bundles = new HashMap<>();
		for (AdxPublisher adxPublisher : publishers) {
			for (AdxUser adxUser : users) {
				BidTarget bidTarget;
				if (ignoreMinorAttributes) {
					bidTarget = new BidTarget(
							((AdxUser) adxUser.clone()).ignoreMinorAttributes(),
							adxPublisher);
				} else {
					bidTarget = new BidTarget(adxUser, adxPublisher);
				}
				BidInfo bidInfo = new AdBidInfo(Math.random()
						* AdxConstants.MAX_AD_PRICE, adNetwork,
						SimpleAdGenerator.getRandomAd());
				bundles.put(bidTarget, bidInfo);
			}
		}
		return bundles;
	}
}
